package me.base.utils;

import cn.hutool.core.lang.Assert;
import lombok.experimental.UtilityClass;
import org.reflections.Reflections;

import java.lang.annotation.Annotation;
import java.lang.reflect.*;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @author :  sylar
 * @FileName :
 * @CreateDate :  2017/11/08
 * @Description :
 * @ReviewedBy :
 * @ReviewedOn :
 * @VersionHistory :
 * @ModifiedBy :
 * @ModifiedDate :
 * @Comments :
 * @CopyRight : COPYRIGHT(c) xxx.com All Rights Reserved
 * *******************************************************************************************
 */
@SuppressWarnings("unchecked")
@UtilityClass
public class ReflectUtil {

    /**
     * 查找所有子类
     *
     * @param prefix     全类名前缀
     * @param superClass 父类
     * @param <T>        父类类型
     * @return 所有子类
     */
    public <T> Set<Class<? extends T>> getSubTypesOf(String prefix, Class<T> superClass) {
        if (prefix == null) {
            prefix = PackageUtil.getAppBasePackageName();
        }
        Reflections reflections = new Reflections(prefix);
        return reflections.getSubTypesOf(superClass);
    }

    /**
     * 获取目标类型的所有字段
     *
     * @param clazz 目标类型
     * @param <T>   泛型类型
     * @return 字段列表
     */
    public <T> List<Field> getAllField(Class<T> clazz) {
        List<Field> fieldList = new ArrayList<>();
        Class<?> tempClass = clazz;
        while (tempClass != null) {
            //当父类为null的时候说明到达了最上层的父类(Object类)
            fieldList.addAll(Arrays.asList(tempClass.getDeclaredFields()));
            //得到父类,然后赋给自己
            tempClass = tempClass.getSuperclass();
        }
        return fieldList;
    }

    /**
     * 获取目标类型的所有字段（按继承顺序）
     *
     * @param clazz 目标类型
     * @param <T>   泛型类型
     * @return 字段列表
     */
    public <T> List<Field> getAllFieldByOrder(Class<T> clazz) {
        List<Field> list = new ArrayList<>();
        Class<?> tempClass = clazz;
        while (tempClass != null) {
            list.addAll(0, Arrays.asList(tempClass.getDeclaredFields()));
            //得到父类,然后赋给自己
            tempClass = tempClass.getSuperclass();
        }
        return list;
    }

    /**
     * 获取目标类型中有指定注解标记的字段列表
     *
     * @param clazz           目标类型
     * @param annotationClass 指定注解
     * @return 字段列表
     */
    public List<Field> getAllFieldByAnnotation(Class<?> clazz, Class<? extends Annotation> annotationClass) {
        List<Field> list = new ArrayList<>();
        Class<?> tempClass = clazz;
        while (tempClass != null) {
            list.addAll(Stream.of(tempClass.getDeclaredFields())
                    .filter(field -> field.isAnnotationPresent(annotationClass))
                    .collect(Collectors.toList()));
            //得到父类,然后赋给自己
            tempClass = tempClass.getSuperclass();
        }
        return list;
    }


    /**
     * 获取对象的指定字段值
     *
     * @param obj       目标对象
     * @param fieldName 目标字段
     * @param <T>       泛型类型
     * @return
     */
    @SuppressWarnings("unchecked")
    public <T> T getFieldValue(Object obj, String fieldName) {
        assert obj != null;
        Class<?> clazz = obj.getClass();
        try {
            return (T) clazz.getDeclaredField(fieldName);
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 为指定字段赋值
     *
     * @param obj        目标对象
     * @param fieldName  目标字段
     * @param fieldValue 字段值
     */
    public void setFieldValue(Object obj, String fieldName, Object fieldValue) {
        assert obj != null;
        assert fieldName != null;
        Class<?> clazz = obj.getClass();
        try {
            Field field = clazz.getDeclaredField(fieldName);
            field.setAccessible(true);
            field.set(obj, fieldValue);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 根据类名生成实例，支持有默认构造方法或单子模式（限定包含getInstance方法）
     *
     * @param classFullName 完整类名
     * @param <T>           泛型类型
     * @return 实例
     */
    @SuppressWarnings("unchecked")
    public <T> T toInstance(String classFullName) {
        Assert.notNull(classFullName, "classFullName is null");
        Object obj = null;

        try {
            Class<?> c = Class.forName(classFullName);
            Constructor<?>[] constructors = c.getConstructors();
            if (constructors.length > 0) {
                //有默认构造方法
                obj = c.newInstance();
            } else {
                //单子模式
                Method method = c.getMethod("getInstance");
                if (Modifier.isStatic(method.getModifiers())) {
                    obj = method.invoke(null);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        return (T) obj;
    }

    /**
     * 获取列表的泛型类型
     *
     * @param list 列表
     * @param <T>  泛型类型
     * @return 泛型类型
     */
    public <T> Class<?> getGenericType(List<T> list) {
        return list.toArray().getClass().getComponentType();
    }

    /**
     * 获取集合的泛型类型
     *
     * @param collection 集合
     * @param <T>        泛型类型
     * @return 泛型类型
     */
    public <T> Class<?> getGenericType(Collection<T> collection) {
        return collection.toArray().getClass().getComponentType();
    }

    /**
     * 获取目标类型的父类的第一个泛型参数的类型
     *
     * @param clazz 目标类型
     * @return 参数位置
     */
    public <T> Class<T> getGenericType(Class<?> clazz) {
        return getGenericType(clazz, 0);
    }

    /**
     * 获取目标类型的父类的泛型参数的类型
     * eg: ClassA<T1,T2> 中的 T1 或 T2 的类型
     *
     * @param clazz 目标类型
     * @param index 参数位置
     * @return 参数的类型
     */
    public <T> Class<T> getGenericType(Class<?> clazz, int index) {
        //返回直接继承的父类（包含泛型参数）
        Type type = clazz.getGenericSuperclass();

        // 不是泛型类
        if (!(type instanceof ParameterizedType)) {
            return null;
        }

        ParameterizedType genType = (ParameterizedType) type;
        Type[] types = genType.getActualTypeArguments();

        Assert.isTrue(index >= 0 && index < types.length, "getGenericType: index error");
        Type objType = types[index];

        return (Class<T>) objType;
    }
}
